
/**
  ******************************************************************************
  * Copyright 2021 The Microbee Authors. All Rights Reserved.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  * 
  * http://www.apache.org/licenses/LICENSE-2.0
  * 
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  * 
  * @file       internal_error.c
  * @author     baiyang
  * @date       2023-1-2
  ******************************************************************************
  */

/*----------------------------------include-----------------------------------*/
#include "internal_error.h"

#include <stdio.h>
#include <assert.h>
#include <math.h>

#include <rtthread.h>

#include <common/console/console.h>
#include <board_config/board_config.h>
/*-----------------------------------macro------------------------------------*/

/*----------------------------------typedef-----------------------------------*/

/*---------------------------------prototype----------------------------------*/

/*----------------------------------variable----------------------------------*/
static const char * const error_bit_descriptions[] = {
    "mapfailure",  // logger_mapfailure
    "miss_struct",  // logger_missing_logstructure
    "write_mssfmt",  // logger_logwrite_missingfmt
    "many_deletes",  // logger_too_many_deletions
    "bad_getfile",  // logger_bad_getfilename
    "panic",
    "flush_no_sem",  // logger_flushing_without_sem
    "bad_curr_blk",  // logger_bad_current_block
    "blkcnt_bad",  // logger_blockcount_mismatch
    "dq_failure",  // logger_dequeue_failure
    "cnstring_nan",  // constraining_nan
    "watchdog_rst",  // watchdog_reset
    "iomcu_reset",
    "iomcu_fail",
    "spi_fail",
    "main_loop_stk",  // main_loop_stuck
    "gcs_bad_link",  // gcs_bad_missionprotocol_link
    "bitmask_range",
    "gcs_offset",
    "i2c_isr",
    "flow_of_ctrl",  // flow_of_control
    "sfs_recursion",  // switch_full_sector_recursion
    "bad_rotation",
    "stack_ovrflw",  // stack_overflow
    "imu_reset",  // imu_reset
    "gpio_isr",
    "mem_guard",
    "dma_fail",
    "params_restored",
    "invalid arguments",
    "storage failed",
};

// actually create the instance:
static struct internal_error instance;

#if CONFIG_HAL_BOARD != HAL_BOARD_SITL_WIN
_Static_assert((1U<<(ARRAY_SIZE(error_bit_descriptions))) == (uint32_t)(internal_error___LAST__), "too few descriptions for bits");
_Static_assert(sizeof(enum internal_error_type) == 4, "error_t should be 32-bit type");

#else
static_assert((1U<<(ARRAY_SIZE(error_bit_descriptions))) == (uint32_t)(internal_error___LAST__), "too few descriptions for bits");

// if you've changed __LAST__ to be 32, then you will want to
// rejig the way we do sanity checks as we don't want to move to a
// 64-bit type for internal_error_type:
static_assert(sizeof(enum internal_error_type) == 4, "error_t should be 32-bit type");
#endif
/*-------------------------------------os-------------------------------------*/

/*----------------------------------function----------------------------------*/
internal_error_const_t internal_error_get_singleton()
{
    return &instance;
}

void internal_error_set(const enum internal_error_type e, uint16_t line)
{
#if CONFIG_HAL_BOARD == HAL_BOARD_SITL_WIN
    switch (e) {
    case internal_error_watchdog_reset:
    case internal_error_main_loop_stuck:
    case internal_error_params_restored:
        // don't panic on these to facilitate watchdog testing
        break;
    default: {
        char buffer[50];
        internal_error_to_string(buffer, ARRAY_SIZE(buffer), e);
        console_panic("internal_error_%s(%u)", buffer, line);
    }
    }
#endif
    instance.internal_errors |= (uint32_t)(e);
    instance.total_error_count++;
    instance.last_line = line;

    //hal.util->persistent_data.internal_errors = internal_errors;
    //hal.util->persistent_data.internal_error_count = total_error_count;
    //hal.util->persistent_data.internal_error_last_line = line;
}

void internal_error_to_string(char *buffer, const uint16_t len, const enum internal_error_type error_code)
{
    uint32_t temp = log2f((int)(error_code));
    rt_strncpy(buffer, error_bit_descriptions[temp], len - 1);
}

void internal_error_errors_as_string(uint8_t *buffer, const uint16_t len)
{
    buffer[0] = 0;
    uint32_t buffer_used = 0;
    for (uint8_t i=0; i<ARRAY_SIZE(error_bit_descriptions); i++) {
        if (buffer_used >= len) {
            break;
        }
        if (instance.internal_errors & (1U<<i)) {
            const char *format;
            if (buffer_used == 0) {
                format = "%s";
            } else {
                format = ",%s";
            }
            const size_t written = rt_snprintf((char*)&buffer[buffer_used],
                                                      len-buffer_used,
                                                      format,
                                                      error_bit_descriptions[i]);
            if (written <= 0) {
                break;
            }
            buffer_used += written;
        }
    }
}

uint16_t internal_error_last_error_line()
{
    return instance.last_line;
}

uint32_t internal_error_count()
{
    return instance.total_error_count;
}

// internal_errors - return mask of internal errors seen
uint32_t internal_error_errors()
{
    return instance.internal_errors;
}
/*------------------------------------test------------------------------------*/


